/* cocos2d for iPhone
 *
 * http://code.google.com/p/cocos2d-iphone
 *
 * Copyright (C) 2008,2009 Ricardo Quesada
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the 'cocos2d for iPhone' license.
 *
 * You will find a copy of this license within the cocos2d for iPhone
 * distribution inside the "LICENSE" file.
 *
 */

/* Idea of decoupling Window from Director taken from OC3D project: http://code.google.com/p/oc3d/
 */
 
// cocos2d imports
#import "Director.h"
#import "Camera.h"
#import "Scheduler.h"
#import "LabelAtlas.h"
#import "ccMacros.h"
#import "ccExceptions.h"

// support imports
#import "Support/glu.h"
#import "Support/OpenGL_Internal.h"
#import "Support/Texture2D.h"
#import "Support/CGPointExtension.h"

#import "Layer.h"

#define kDefaultFPS		60.0	// 60 frames per second


@interface Director (Private)
-(BOOL)isOpenGLAttached;
-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect;

-(void) initGLDefaultValues;

-(void) mainLoop;
-(void) setNextScene;
// rotates the screen if Landscape mode is activated
-(void) applyLandscape;
// shows the FPS in the screen
-(void) showFPS;
// calculates delta time since last time it was called
-(void) calculateDeltaTime;


@end

@implementation Director

@synthesize animationInterval;
@synthesize runningScene = runningScene_;
@synthesize displayFPS, eventsEnabled;
@synthesize openGLView=openGLView_;
@synthesize pixelFormat=pixelFormat_;
@synthesize nextDeltaTimeZero=nextDeltaTimeZero_;

//
// singleton stuff
//
static Director *_sharedDirector = nil;

+ (Director *)sharedDirector
{
	@synchronized([Director class])
	{
		if (!_sharedDirector)
			[[self alloc] init];
		
		return _sharedDirector;
	}
	// to avoid compiler warning
	return nil;
}

// This function was created to avoid confussion for the users
// Calling [FastDirector sharedDirector] is enough, but is somewhat
// confusing since the user needs to understand what's under the hood
+ (void) useFastDirector
{
	NSAssert(_sharedDirector==nil, @"A Director was alloced. To use Fast Director this must be the first call to Director");
	[FastDirector sharedDirector];
}

+(id)alloc
{
	@synchronized([Director class])
	{
		NSAssert(_sharedDirector == nil, @"Attempted to allocate a second instance of a singleton.");
		_sharedDirector = [super alloc];
		return _sharedDirector;
	}
	// to avoid compiler warning
	return nil;
}

- (id) init
{   
	//Create a full-screen window

	// default values
	pixelFormat_ = kRGB565;
	depthBufferFormat_ = 0;

	// scenes
	runningScene_ = nil;
	nextScene = nil;
	
	oldAnimationInterval = animationInterval = 1.0 / kDefaultFPS;
	eventHandlers = [[NSMutableArray arrayWithCapacity:8] retain];
	scenesStack_ = [[NSMutableArray arrayWithCapacity:10] retain];
	
	// landscape
	landscape = NO;
	
	// FPS
	displayFPS = NO;
	frames = 0;
	
	// paused ?
	paused = NO;
	
	// touch events enabled ?
	eventsEnabled = YES;
	
	return self;
}

- (void) dealloc
{
	CCLOG( @"deallocing %@", self);

#ifdef FAST_FPS_DISPLAY
	[FPSLabel release];
#endif
	[eventHandlers release];
	[runningScene_ release];
	[scenesStack_ release];
	
	[super dealloc];
}

-(void) initGLDefaultValues
{
	// This method SHOULD be called only after openGLView_ was initialized
	NSAssert( openGLView_, @"openGLView_ must be initialized");

	[self setAlphaBlending: YES];
	[self setDepthTest: YES];
	[self setDefaultProjection];
	
	// set other opengl default values
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	
#ifdef FAST_FPS_DISPLAY
	FPSLabel = [[LabelAtlas labelAtlasWithString:@"00.0" charMapFile:@"fps_images.png" itemWidth:16 itemHeight:24 startCharMap:'.'] retain];
#endif	
}

//
// main loop
//
- (void) mainLoop
{    
	/* clear window */
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	/* calculate "global" dt */
	[self calculateDeltaTime];
	if( ! paused )
		[[Scheduler sharedScheduler] tick: dt];
	
	
	/* to avoid flickr, nextScene MUST be here: after tick and before draw */
	if( nextScene )
		[self setNextScene];
	
	glPushMatrix();
	
	[self applyLandscape];
	
	/* draw the scene */
	[runningScene_ visit];
	if( displayFPS )
		[self showFPS];
	
		glPopMatrix();
	
	/* swap buffers */
	[openGLView_ swapBuffers];	
}

-(void) calculateDeltaTime
{
	struct timeval now;
	
	if( gettimeofday( &now, NULL) != 0 ) {
		NSException* myException = [NSException
									exceptionWithName:@"GetTimeOfDay"
									reason:@"GetTimeOfDay abnormal error"
									userInfo:nil];
		@throw myException;
	}
	
	// new delta time
	if( nextDeltaTimeZero_ ) {
		dt = 0;
		nextDeltaTimeZero_ = NO;
	} else {
		dt = (now.tv_sec - lastUpdate.tv_sec) + (now.tv_usec - lastUpdate.tv_usec) / 1000000.0f;
		dt = MAX(0,dt);
	}
	
	lastUpdate = now;	
}

#pragma mark Director Scene iPhone Specific

-(void) setPixelFormat: (tPixelFormat) format
{	
	if( [self isOpenGLAttached] ) {
		NSException* myException = [NSException
									exceptionWithName:@"DirectorAlreadyInitialized"
									reason:@"Can't change the pixel format after the director was initialized"
									userInfo:nil];
		@throw myException;		
	}
	
	pixelFormat_ = format;
}

-(void) setDepthBufferFormat: (tDepthBufferFormat) format
{
	if( [self isOpenGLAttached] ) {
		NSException* myException = [NSException
                                  exceptionWithName:@"DirectorAlreadyInitialized"
                                  reason:@"Can't change the depth buffer format after the director was initialized"
                                  userInfo:nil];
		@throw myException;		
	}

   depthBufferFormat_ = format;
}

#pragma mark Director Scene OpenGL Helper

- (void) setDefaultProjection
{
//	[self set2Dprojection];
	[self set3Dprojection];
}

-(void)set2Dprojection
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrthof(0, openGLView_.frame.size.width, 0, openGLView_.frame.size.height, -1, 1);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

// set a 3d projection matrix
-(void)set3Dprojection
{
	glViewport(0, 0, openGLView_.frame.size.width, openGLView_.frame.size.height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60, (GLfloat)openGLView_.frame.size.width/openGLView_.frame.size.height, 0.5f, 1500.0f);
	
	glMatrixMode(GL_MODELVIEW);	
	glLoadIdentity();
	gluLookAt( openGLView_.frame.size.width/2, openGLView_.frame.size.height/2, [Camera getZEye],
			  openGLView_.frame.size.width/2, openGLView_.frame.size.height/2, 0,
			  0.0f, 1.0f, 0.0f);
}

- (void) setAlphaBlending: (BOOL) on
{
	if (on) {
		glEnable(GL_BLEND);
		glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
	} else
		glDisable(GL_BLEND);
}

- (void) setTexture2D: (BOOL) on
{
	if (on)
		glEnable(GL_TEXTURE_2D);
	else
		glDisable(GL_TEXTURE_2D);
}

- (void) setDepthTest: (BOOL) on
{
	if (on) {
		glClearDepthf(1.0f);
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	} else
		glDisable( GL_DEPTH_TEST );
}

#pragma mark Director Integration with a UIKit view

// is the view currently attached
-(BOOL)isOpenGLAttached
{
	return ([openGLView_ superview]!=nil);
}

// detach or attach to a view or a window
-(BOOL)detach
{
	// check if the view is attached
	if(![self isOpenGLAttached])
	{
		// the view is not attached
		NSException* myException = [NSException
									exceptionWithName:kccException_OpenGLViewNotAttached
									reason:@"Can't detach the OpenGL View, because it is not attached. Attach it first."
									userInfo:nil];
		@throw myException;
		
		return NO;
	}
	
	// remove from the superview
	[openGLView_ removeFromSuperview];
	
	// check if the view is not attached anymore
	if(![self isOpenGLAttached])
	{
		return YES;
	}
	
	// the view is still attached
	NSException* myException = [NSException
								exceptionWithName:kccException_OpenGLViewCantDetach
								reason:@"Can't detach the OpenGL View, it is still attached to the superview."
								userInfo:nil];
	@throw myException;
	
	return NO;
}

-(BOOL)attachInWindow:(UIWindow *)window
{
	if([self initOpenGLViewWithView:window withFrame:[window bounds]])
	{
		return YES;
	}
	
	return NO;
}

-(BOOL)attachInView:(UIView *)view
{
	if([self initOpenGLViewWithView:view withFrame:[view bounds]])
	{
		return YES;
	}
	
	return NO;
}

-(BOOL)attachInView:(UIView *)view withFrame:(CGRect)frame
{
	if([self initOpenGLViewWithView:view withFrame:frame])
	{
		return YES;
	}
	
	return NO;
}

-(BOOL)initOpenGLViewWithView:(UIView *)view withFrame:(CGRect)rect
{
	// check if the view is not attached
	if([self isOpenGLAttached])
	{
		// the view is already attached
		NSException* myException = [NSException
									exceptionWithName:kccException_OpenGLViewAlreadyAttached
									reason:@"Can't re-attach the OpenGL View, because it is already attached. Detach it first."
									userInfo:nil];
		@throw myException;
		
		return NO;
	}
	
	// check if the view is not initialized
	if(!openGLView_)
	{
		// define the pixel format
		NSString	*pFormat = kEAGLColorFormatRGB565;
	    GLuint		depthFormat = 0;
		
		if(pixelFormat_==kRGBA8)
			pFormat = kEAGLColorFormatRGBA8;
		
		if(depthBufferFormat_ == kDepthBuffer16)
			depthFormat = GL_DEPTH_COMPONENT16_OES;
		else if(depthBufferFormat_ == kDepthBuffer24)
			depthFormat = GL_DEPTH_COMPONENT24_OES;
		
		// alloc and init the opengl view
		openGLView_ = [[EAGLView alloc] initWithFrame:rect pixelFormat:pFormat depthFormat:depthFormat preserveBackbuffer:NO];
		
		// check if the view was alloced and initialized
		if(!openGLView_)
		{
			// the view was not created
			NSException* myException = [NSException
										exceptionWithName:kccException_OpenGLViewCantInit
										reason:@"Could not alloc and init the OpenGL View."
										userInfo:nil];
			@throw myException;
			
			return NO;
		}
		
		// set autoresizing enabled when attaching the glview to another view
		[openGLView_ setAutoresizesEAGLSurface:YES];
		
		// set the touch delegate of the glview to self
		[openGLView_ setTouchDelegate:self];
	}
	else
	{
		// set the (new) frame of the glview
		[openGLView_ setFrame:rect];
	}
	
	// check if the superview has touchs enabled and enable it in our view
	if([view isUserInteractionEnabled])
	{
		[openGLView_ setUserInteractionEnabled:YES];
		[self setEventsEnabled:YES];
	}
	else
	{
		[openGLView_ setUserInteractionEnabled:NO];
		[self setEventsEnabled:NO];
	}
	
	// check if multi touches are enabled and set them
	if([view isMultipleTouchEnabled])
	{
		[openGLView_ setMultipleTouchEnabled:YES];
	}
	else
	{
		[openGLView_ setMultipleTouchEnabled:NO];
	}
	
	// add the glview to his (new) superview
	[view addSubview:openGLView_];
	
	// set the background color of the glview
	//	[backgroundColor setOpenGLClearColor];
	
	// check if the glview is attached now
	if([self isOpenGLAttached])
	{
		[self initGLDefaultValues];
		return YES;
	}
	
	// the glview is not attached, but it should have been
	NSException* myException = [NSException
								exceptionWithName:kccException_OpenGLViewCantAttach
								reason:@"Can't attach the OpenGL View."
								userInfo:nil];
	@throw myException;
	
	return NO;
}

#pragma mark Director Scene Landscape

// convert a coordinate from uikit to opengl
-(CGPoint)convertCoordinate:(CGPoint)p
{
	int newY = openGLView_.frame.size.height - p.y;
	
	CGPoint ret = ccp( p.x, newY );
	if( ! landscape )
	{
		ret = ret;
	}
	else 
	{
#ifdef LANDSCAPE_LEFT
		ret.x = p.y;
		ret.y = p.x;
#else
		ret.x = p.y;
		ret.y = openGLView_.frame.size.width -p.x;
#endif // LANDSCAPE_LEFT
	}
	
	return ret;
}

// get the current size of the glview
-(CGSize)winSize
{
	CGSize s = openGLView_.frame.size;
	if( landscape ) {
		// swap x,y in landscape mode
		s.width = openGLView_.frame.size.height;
		s.height = openGLView_.frame.size.width;
	}
	return s;
}

// return  the current frame size
-(CGSize)displaySize
{
	return openGLView_.frame.size;
}

- (BOOL) landscape
{
	return landscape;
}

- (void) setLandscape: (BOOL) on
{
	if( on != landscape ) {
		landscape = on;
		if( landscape )
#ifdef LANDSCAPE_LEFT
			[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeRight animated:NO];
#else
			[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationLandscapeLeft animated:NO];
#endif
		else
			[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:NO];

	}
	return;
}

-(void) applyLandscape
{
	if( landscape ) {
		glTranslatef(160,240,0);
		
#ifdef LANDSCAPE_LEFT
		glRotatef(-90,0,0,1);
		glTranslatef(-240,-160,0);
#else		
		// rotate left
		glRotatef(90,0,0,1);
		glTranslatef(-240,-160,0);
#endif // LANDSCAPE_LEFT
	}	
}

#pragma mark Director Scene Management

- (void)runWithScene:(Scene*) scene
{
	NSAssert( scene != nil, @"Argument must be non-nil");
	NSAssert( runningScene_ == nil, @"You can't run an scene if another Scene is running. Use replaceScene or pushScene instead");
	
	[self pushScene:scene];
	[self startAnimation];
}

-(void) replaceScene: (Scene*) scene
{
	NSAssert( scene != nil, @"Argument must be non-nil");

	NSUInteger index = [scenesStack_ count];

	[scenesStack_ replaceObjectAtIndex:index-1 withObject:scene];
	nextScene = scene;	// nextScene is a weak ref
}

- (void) pushScene: (Scene*) scene
{
	NSAssert( scene != nil, @"Argument must be non-nil");

	[scenesStack_ addObject: scene];
	nextScene = scene;	// nextScene is a weak ref
}

-(void) popScene
{	
	NSAssert( runningScene_ != nil, @"A running Scene is needed");

	[scenesStack_ removeLastObject];
	NSUInteger c = [scenesStack_ count];
	
	if( c == 0 ) {
		[self end];
	} else {
		nextScene = [scenesStack_ objectAtIndex:c-1];
	}
}

-(void) end
{
	// remove all objects, but don't release it.
	// runWithScene might be executed after 'end'.
	[scenesStack_ removeAllObjects];

	[runningScene_ onExit];
	[runningScene_ release];
	runningScene_ = nil;
	nextScene = nil;

	// don't release the event handlers
	// They are needed in case the director is run again
	[eventHandlers removeAllObjects];

	[self stopAnimation];
	[self detach];
	

	// dont call terminate
	// an application might want to kill the director
	// without quiting the application
//	if( [[UIApplication sharedApplication] respondsToSelector:@selector(terminate)] )
//		[[UIApplication sharedApplication] performSelector:@selector(terminate)];
}

-(void) setNextScene
{
	[runningScene_ onExit];
	[runningScene_ release];
	
	runningScene_ = [nextScene retain];
	nextScene = nil;

	[runningScene_ onEnter];
}

-(void) pause
{
	if( paused )
		return;

	oldAnimationInterval = animationInterval;
	
	// when paused, don't consume CPU
	[self setAnimationInterval:1/4.0];
	paused = YES;
}

-(void) resume
{
	if( ! paused )
		return;
	
	[self setAnimationInterval: oldAnimationInterval];

	if( gettimeofday( &lastUpdate, NULL) != 0 ) {
		NSException* myException = [NSException
									exceptionWithName:@"GetTimeOfDay"
									reason:@"GetTimeOfDay abnormal error"
									userInfo:nil];
		@throw myException;
	}
	
	paused = NO;
	dt = 0;
}

- (void)startAnimation
{
	NSAssert( animationTimer == nil, @"animationTimer must be nil. Calling startAnimation twice?");

	if( gettimeofday( &lastUpdate, NULL) != 0 ) {
		NSException* myException = [NSException
									exceptionWithName:@"GetTimeOfDay"
									reason:@"GetTimeOfDay abnormal error"
									userInfo:nil];
		@throw myException;
	}
	
	

	animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];

//
//	If you want to attach the opengl view into UIScrollView
//  uncomment this line to prevent 'freezing'.
//	It doesn't work on with the Fast Director
//
//	[[NSRunLoop currentRunLoop] addTimer:animationTimer
//								 forMode:NSRunLoopCommonModes];
}

- (void)stopAnimation
{
	[animationTimer invalidate];
	animationTimer = nil;
}

- (void)setAnimationInterval:(NSTimeInterval)interval
{
	animationInterval = interval;
	
	if(animationTimer) {
		[self stopAnimation];
		[self startAnimation];
	}
}

#pragma mark Director Events

-(void) addEventHandler:(id<TouchEventsDelegate>) delegate
{
	NSAssert( delegate != nil, @"Director.addEventHandler: delegate must be non nil");	
	[eventHandlers insertObject:delegate atIndex:0];
}

-(void) removeEventHandler:(id<TouchEventsDelegate>) delegate
{
	NSAssert( delegate != nil, @"Director.removeEventHandler: delegate must be non nil");
	[eventHandlers removeObject:delegate];
}

//
// multi touch proxies
//
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	if( eventsEnabled ) {
		NSArray *copyArray = [eventHandlers copy];
		for( id eventHandler in copyArray ) {
			if( [eventHandler respondsToSelector:@selector(ccTouchesBegan:withEvent:)] ) {
				if( [eventHandler ccTouchesBegan:touches withEvent:event] == kEventHandled )
					break;
			}
		}
		
		[copyArray release];
	}	
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	if( eventsEnabled ) {
		NSArray *copyArray = [eventHandlers copy];
		for( id eventHandler in copyArray ) {
			if( [eventHandler respondsToSelector:@selector(ccTouchesMoved:withEvent:)] ) {
				if( [eventHandler ccTouchesMoved:touches withEvent:event] == kEventHandled )
					break;
			}
		}
		[copyArray release];
	}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	if( eventsEnabled ) {
		NSArray *copyArray = [eventHandlers copy];
		for( id eventHandler in copyArray ) {
			if( [eventHandler respondsToSelector:@selector(ccTouchesEnded:withEvent:)] ) {
				if( [eventHandler ccTouchesEnded:touches withEvent:event] == kEventHandled )
					break;
			}
		}
		[copyArray release];
	}
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
	if( eventsEnabled )  {
		NSArray *copyArray = [eventHandlers copy];
		for( id eventHandler in copyArray ) {
			if( [eventHandler respondsToSelector:@selector(ccTouchesCancelled:withEvent:)] ) {
				if( [eventHandler ccTouchesCancelled:touches withEvent:event] == kEventHandled )
					break;
			}
		}
		[copyArray release];
	}
}


#ifdef FAST_FPS_DISPLAY

// display the FPS using a LabelAtlas
// updates the FPS every frame
-(void) showFPS
{
	frames++;
	accumDt += dt;
	
	if ( accumDt > 0.1)  {
		frameRate = frames/accumDt;
		frames = 0;
		accumDt = 0;
	}
		
	NSString *str = [NSString stringWithFormat:@"%.1f",frameRate];
//	glTranslatef(10.0, 10.0, 0);
	[FPSLabel setString:str];
	[FPSLabel draw];
}
#else
// display the FPS using a manually generated Texture (very slow)
// updates the FPS 3 times per second aprox.
-(void) showFPS
{
	frames++;
	accumDt += dt;
	
	if ( accumDt > 0.3)  {
		frameRate = frames/accumDt;
		frames = 0;
		accumDt = 0;
	}
	
	NSString *str = [NSString stringWithFormat:@"%.2f",frameRate];
	Texture2D *texture = [[Texture2D alloc] initWithString:str dimensions:CGSizeMake(100,30) alignment:UITextAlignmentLeft fontName:@"Arial" fontSize:24];
	glEnable(GL_TEXTURE_2D);
	glEnableClientState( GL_VERTEX_ARRAY);
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );
	
	glColor4ub(224,224,244,200);
	[texture drawAtPoint: ccp(5,2)];
	[texture release];
	
	glDisable(GL_TEXTURE_2D);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
#endif

@end

#pragma mark -
#pragma mark Director FastDirector

@implementation FastDirector

- (id) init
{
	CCLOG(@"Using Fast Director");

	if(( self = [super init] )) {
		isRunning = NO;
		
		// XXX:
		// XXX: Don't create any autorelease object before calling "fast director"
		// XXX: else it will be leaked
		// XXX:
		autoreleasePool = [NSAutoreleasePool new];
	}

	return self;
}

- (void) startAnimation
{
	// XXX:
	// XXX: release autorelease objects created
	// XXX: between "use fast director" and "runWithScene"
	// XXX:
	[autoreleasePool release];
	autoreleasePool = nil;

	if ( gettimeofday( &lastUpdate, NULL) != 0 ) {
		NSException* myException = [NSException
									exceptionWithName:@"GetTimeOfDay"
									reason:@"GetTimeOfDay abnormal error"
									userInfo:nil];
		@throw myException;
	}
	

	isRunning = YES;

	SEL selector = @selector(preMainLoop);
	NSMethodSignature* sig = [[[Director sharedDirector] class]
							  instanceMethodSignatureForSelector:selector];
	NSInvocation* invocation = [NSInvocation
								invocationWithMethodSignature:sig];
	[invocation setTarget:[Director sharedDirector]];
	[invocation setSelector:selector];
	[invocation performSelectorOnMainThread:@selector(invokeWithTarget:)
								 withObject:[Director sharedDirector] waitUntilDone:NO];	
}

-(void) preMainLoop
{
	while (isRunning) {
	
		NSAutoreleasePool *loopPool = [NSAutoreleasePool new];

		while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);

		if (paused) {
			usleep(250000); // Sleep for a quarter of a second (250,000 microseconds) so that the framerate is 4 fps.
		}
		
		[self mainLoop];
		
		while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE) == kCFRunLoopRunHandledSource);

		[loopPool release];
	}	
}
- (void) stopAnimation
{
	isRunning = NO;
}

- (void)setAnimationInterval:(NSTimeInterval)interval
{
	NSLog(@"FastDirectory doesn't support setAnimationInterval, yet");
}
@end

